/**
 * Project: apollo-base-commons
 * 
 * File Created at 2016年10月25日
 * 
 * Copyright 2015-2016 dx.com Croporation Limited.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * DongXue software Company. ("Confidential Information").  You shall not
 * disclose such Confidential Information and shall use it only in
 * accordance with the terms of the license agreement you entered into
 * with dx.com.
 */
package com.dx.pf.commons.async.proxy;

import java.lang.reflect.Method;
import java.util.Map;

import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

import com.dx.pf.commons.async.bean.AsyncMethod;
import com.dx.pf.commons.async.core.AsyncExecutor;
import com.dx.pf.commons.async.pool.AsyncFutureTask;
import com.dx.pf.commons.async.pool.AsyncPoolCallable;
import com.dx.pf.commons.async.pool.AsyncRunnable;
import com.dx.pf.commons.async.temple.AsyncTemplate;
import com.dx.pf.commons.cache.AsyncProxyCache;
import com.dx.pf.commons.constants.AsyncConstant;
import com.dx.pf.commons.exceptions.AsyncException;
import com.dx.pf.commons.log.Logger;
import com.dx.pf.commons.reflect.ClassUtil;
import com.dx.pf.commons.reflect.ReflectUtil;

/** 
* @ClassName: AsyncMethodInterceptor 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author wuzhenfang(wzfbj2008@163.com)
* @date 2016年10月25日 下午3:57:38 
* @version V1.0 
*/
public class AsyncMethodInterceptor implements MethodInterceptor {

	private final static Logger logger = Logger.getLogger(AsyncMethodInterceptor.class);

	private long timeout;

	private Object targetObject;

	private boolean all;

	public AsyncMethodInterceptor(Object targetObject, long timeout, boolean all) {
		this.timeout = timeout;
		this.targetObject = targetObject;
		this.all = all;
	}

	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		if (AsyncConstant.ASYNC_DEFAULT_TRACE_LOG) {
			logger.debug("start call obejct:"+ClassUtil.getClass(targetObject).getName()+" method: " + ClassUtil.buildMethod(method));
		}
		if (AsyncExecutor.isDestroyed()) {
			return ReflectUtil.invoke(targetObject, args, method);
		}
		String key = ClassUtil.buildkey(targetObject, method);
		if (!all && !AsyncProxyCache.containMethod(key)) {
			return ReflectUtil.invoke(targetObject, args, method);
		}

		final Object finObj = targetObject;
		final Object[] finArgs = args;
		final Method finMethod = method;

		long timeout = this.timeout;

		Class<?> returnClass = method.getReturnType();
		if (Void.TYPE.isAssignableFrom(returnClass)) {
			AsyncTemplate.execute(new AsyncRunnable() {
				@Override
				public void doAsync(Map<String, Object> dataMap) {
					try {
						ReflectUtil.invoke(finObj, finArgs, finMethod);
					} catch (Throwable e) {
						logger.error("async runnable invoke error", e);
					}
				}
			});
			return null;
		}

		final AsyncMethod asyncMethod = AsyncProxyCache.getAsyncMethod(key);
		if (asyncMethod != null) {
			timeout = asyncMethod.getTimeout();
		}
		AsyncFutureTask<Object> future = AsyncExecutor.submit(new AsyncPoolCallable<Object>() {
			public Object call() throws Exception {
				Object object = null;
				try {
					if (asyncMethod != null) {
						if (AsyncConstant.ASYNC_DEFAULT_TRACE_LOG) {
							logger.debug("start async call object:"+asyncMethod.getObject().getClass().getName()+" asyncMethod: "+
									ClassUtil.buildMethod(asyncMethod.getMethod()));
						}
						object = ReflectUtil.invoke(asyncMethod.getObject(), finArgs, asyncMethod.getMethod());
						if (AsyncConstant.ASYNC_DEFAULT_TRACE_LOG) {
							logger.debug("end async call object:"+asyncMethod.getObject().getClass().getName()+" asyncMethod: " + ClassUtil.buildMethod(asyncMethod.getMethod()));
						}
					} else {
						if (AsyncConstant.ASYNC_DEFAULT_TRACE_LOG) {
							logger.debug("start call object:"+finObj.getClass().getName()+" method: " + ClassUtil.buildMethod(finMethod));
						}
						object = ReflectUtil.invoke(finObj, finArgs, finMethod);
						if (AsyncConstant.ASYNC_DEFAULT_TRACE_LOG) {
							logger.debug("end call object:"+finObj.getClass().getName()+" method: " + ClassUtil.buildMethod(finMethod));
						}
					}
					return object;
				} catch (Throwable e) {
					throw new AsyncException("future invoke error", e);
				}
			}
		});
		return new AsyncResultProxy(future).buildProxy(returnClass, timeout, true);

	}
}
